#pragma once 
#include<iostream>
#include<string>
#include<vector>
#include<unordered_map>
#include<sstream>
#include<algorithm>
#include<cstdlib>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/sendfile.h>
#include<sys/wait.h>
#include"log.hpp"
#include"util.hpp"

#define WWWROOT "wwwroot"
#define HOMEHTML "index.html"
#define ENDTION "HTTP/1.0"
//请求报文
class RequestHttp
{
  public:
    /*
     请求行
     请求报头
     请求空行
     请求正文
    */
    string request_line;
    vector<string> request_head;
    string request_blank;
    string request_text;

    /* 
      方法
      url 
      版本
    */
    string method;
    string url;
    string version;
    
    /*
      访问路径
      参数
    */
    string path;
    string parameter;
    
    /*
      请求报头的字段(kv结构) 
    */
    unordered_map<string,string> headkv;

    bool request_cgi;
    int content_length;
    int code;
    string request_suffix;
    RequestHttp():request_cgi(false)
    {}
    ~RequestHttp()
    {}
};

class ResponseHttp
{
  public:
    string response_line;
    vector<string> response_head;
    string response_blank;
    string response_text;
  
    size_t response_size;

    ResponseHttp():response_blank("\r\n")
    {}
    ~ResponseHttp()
    {}
};

class Endterminal
{
  private:
    RequestHttp request;
    ResponseHttp response;
    
    int sock;
    bool stop;
		int fd;
  public:
    Endterminal(int _sock):sock(_sock),stop(false),fd(-1)
    {}
    ~Endterminal()
    {
      if(sock>=0)
        close(sock);
    }
  
  public:
    bool Recvrequestline()
    {
      if(Getline(sock,request.request_line)>0)
      {
        request.request_line.resize(request.request_line.size()-1);
        stringstream ss(request.request_line);
        ss>>request.method>>request.url>>request.version;    
        transform(request.method.begin(),request.method.end(),request.method.begin(),::toupper);

        LOG(INFO,request.request_line);
        LOG(INFO,request.method);
        LOG(INFO,request.url);
        LOG(INFO,request.version);
      }
      else 
      {
        stop=true;
      }
      return stop;
    }

    bool Recvrequesthead()
    {
      if(!stop)
      {
        string s;
        while(true)
        {
          s.clear();
          if(Getline(sock,s)<=0)
          {
            stop=true;
            return stop;
          }
          if(s!="\n")
          {
            s.resize(s.size()-1);
            request.request_head.push_back(s);
          }
          else if(s=="\n")
            break;
        }
        
        request.request_blank="\n";

        for(auto& s:request.request_head)
        {
          int pos=s.find(':');
          string k=s.substr(0,pos);
          string v=s.substr(pos+2);
          request.headkv.insert({k,v});
        }
        
        for(auto& s:request.request_head)
          LOG(INFO,s);
      }
      return stop;
    }
    
    bool Revcrequesttet()
    {
      if(!stop)
      {
        if(Getcontentlength())
        {
          int length=request.content_length;
          while(length)
          {
            char ch;
            ssize_t s= recv(sock,&ch,1,0);
            if(s>0)
            {
              request.request_text.push_back(ch);
            }
            else 
            {
              stop=true;
              break;
            }
            length--;
          }
          LOG(INFO,request.request_text);
        }
      }
      return stop;
    }

    void Analysisrequest()
    {
      if(stop) return;
      string temp_path;
      struct stat st;
      if(request.method!="GET" && request.method!="POST")
      {
        LOG(ERROR,"NOT_MOTHOD");
        request.code=404;
        goto END;
      }
      if(request.method=="GET")
      {
         int pos=request.url.find('?');
         if(pos!=string::npos)
         {
           request.path=request.url.substr(0,pos);
           request.parameter=request.url.substr(pos+1);
           request.request_cgi=true;
         }
         else 
         {
           request.path=request.url;
         }
      }
      else 
      {
        request.path=request.url;
        request.request_cgi=true;
      }
      
      temp_path=WWWROOT;
      temp_path+=request.path;
      request.path=temp_path;
      if(request.path[request.path.size()-1]=='/')
        request.path+=HOMEHTML;
      
      if(stat(request.path.c_str(),&st)==0)
      {
        if(S_ISDIR(st.st_mode))
        {
            request.path+='/';
            request.path+=HOMEHTML;
            stat(request.path.c_str(),&st);
        }
        if(st.st_mode&S_IXUSR ||st.st_mode&S_IXGRP ||st.st_mode&S_IXOTH )
            request.request_cgi=true;
        response.response_size=st.st_size;
        int pos=request.path.rfind('.');
        if(pos!=std::string::npos)
            request.request_suffix=request.path.substr(pos);
         else 
            request.request_suffix=".html";    
      }
      else 
      {
        LOG(ERROR,"NOT_FROUNT");
        request.code=404;
        goto END;
      }
      LOG(INFO,request.path);
      if(request.request_cgi)
      {
        request.code=Cgi();
      }
      else 
      {
        request.code=NotCgi();  
      }
END:
      Builderesponse();
      return;
    }

    void Builderesponse()
    {
      cout<<"构建响应"<<endl;
      response.response_line=request.version;
      response.response_line+=" ";
      response.response_line+=to_string(request.code);
      response.response_line+=" ";
      response.response_line+=Getcode(request.code);
      response.response_line+="\r\n";

      switch(request.code)
			{
				case 200:
				{
					Bulie200();
					break;
				}
				case 400:
				{
          string path="404.html";
					Bulie404(path);
					break;
				}
				default:
					break;
			}
    }

    void Sendresponse()
    {
			send(sock,response.response_line.c_str(),response.response_line.size(),0);
			for(auto& s:response.response_head)
			{
				send(sock,s.c_str(),s.size(),0);
			}
			send(sock,response.response_blank.c_str(),response.response_blank.size(),0);
			
			if(request.request_cgi)
			{
				
			}
			else 
			{
				sendfile(sock,fd,nullptr,response.response_size);
				close(fd);
			}
    }
    
  private:
    /*
     *1.需要调用cgi程序进行数据分析
     *2.得知道路径，和方法
     *3.使用程序替换来进行，创建父子进程，通过管道来进行数据传输
     *需要双管道来进行父子进程的数据传输
     */ 
    int Cgi()
    {
      cout<<"调用cgi"<<endl;
      
      int code=200;
      string& path=request.path;
      string& method=request.method;
      
      string parameter_env;
      string method_env;
      string length_env;

      int input[2];
      int output[2];
      if(pipe(input)<0)
      {
        code=404;
        return code;
      }
      if(pipe(output)<0)
      {
        code=404;
        return code;
      }

      pid_t id=fork();
      if(id==0)
      {
        close(input[0]);//input[1]写
        close(output[1]);//output[0]读

        dup2(input[1],1);
        dup2(output[0],0);
        
        method_env="METHOD=";
        method_env+=method;
        putenv((char*)method_env.c_str());

        if(method=="GET"){
          parameter_env="PARAMETER=";
          parameter_env+=request.parameter;
          putenv((char*)parameter_env.c_str());
        }
        if(method=="POST"){
          length_env="CONTENT_LENGTH=";
          length_env+=to_string(request.content_length);
          putenv((char*)length_env.c_str());
        }

        execl(path.c_str(),path.c_str(),nullptr);
        exit(1);
      }
      
      close(input[1]);//intput[0]读
      close(output[0]);//output[1]写
      if(method=="POST")
      {
        const char* str=request.request_text.c_str();
        int start=0;
        int size=0;
        while((size=write(output[1],str+start,request.request_text.size()-start))>0)
        {
          start+=size;
        }
      }
      char ch=0;
      while(read(input[0],&ch,1)>0)
      {
        response.response_text.push_back(ch);
      }
      int status=0;
      pid_t ret=waitpid(id,&status,0);
      if(ret==id)
      {
        if(WIFEXITED(status))
        {
          if(WEXITSTATUS(status)==0)
          {
            code=200;
          }
          else
          {
            code=404;
          }
        }
        else
        {
          code=404;
        }
      }
      close(input[0]);
      close(output[1]);
            
      return code;
    }
    int NotCgi()
    {
			cout<<"没有调用cgi"<<endl;
			fd=open(request.path.c_str(),O_RDONLY);
			if(fd>=0)
			{
				return 200;
			}
			return 404;
    }

  private:
    bool Getcontentlength()
    {
      if(request.method=="POST")
      {
        auto it=request.headkv.find("Content-Length");
        if(it!=request.headkv.end())
        {
          request.content_length=atoi(it->second.c_str());
          return true;
        }
      }
      return false;
    }
    string Getcode(int code)
    {
      switch(code)
      {
        case 200:
          return "OK";
        case 404:
          return "NOT_FROUNT";
        default:
          break;
      }
    }
    string Getsuffix(const string& suffix)
    {
      static unordered_map<string,string> mp={
            {".html","text/html"},
            {".css","text/css"},
            {".is","application/javascript"},
            {".jpg","application/x-jpg"},
            {".xml","application/xml"}
      };
      auto it=mp.find(suffix);
      if(it!=mp.end())
      {
        return it->second;
      }
      else
      {
        return "text/html";
      }
    }
    void Bulie200()
    {
     	string suffix="Content-Type:";
      suffix+=" ";
      suffix+=Getsuffix(request.request_suffix);
      suffix+="\r\n";
      response.response_head.push_back(suffix);

      string content_length="Content-Length: ";
      if(request.request_cgi)
			{
        content_length+=to_string(response.response_text.size());
      }
      else
			{
        content_length+=to_string(response.response_size);
      }
      content_length+="\r\n";
      response.response_head.push_back(content_length);
    }

    void Bulie404(string& temp_path)
    {
      request.request_cgi=false;
      LOG(INFO,temp_path);
      fd=open(temp_path.c_str(),O_RDONLY);
      if(fd>=0)
      {
        LOG(INFO,"进入了404,并打开了文件...");
        string suffix="Content-Type: text/html";
        suffix+="\r\n";
        response.response_head.push_back(suffix);

        string content_length="Content-Length:";
        content_length+=" ";
        struct stat st;
        stat(temp_path.c_str(),&st);
        response.response_size=st.st_size;
        content_length+=to_string(st.st_size);
        content_length="\r\n";
        response.response_head.push_back(content_length);
      }
    }
    //……
};


class CallBack{
  private:
    
  public:
    CallBack(){}
    ~CallBack(){}

    void operator()(int sock)
    {
      Endterminal* end=new Endterminal(sock);
      end->Recvrequestline();
      end->Recvrequesthead();
      end->Revcrequesttet();
      end->Analysisrequest();
			end->Sendresponse();
      std::cout<<"任务完成"<<std::endl;
    }
};
